/**
 * Copyright By Nanjing Fujitsu Nanda Software Technology Co., Ltd
 * 上午9:38:22
 * MyTokenBasedRememberMeServices.java
 * 
 */
package com.fujitsu.nanjing.tieba.security;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;

import com.fujitsu.nanjing.tieba.constrant.CommonParamter;
import com.fujitsu.nanjing.tieba.ibator.CoreUser;
import com.fujitsu.nanjing.tieba.service.UserService;

/**
 * @author Administrator
 * 
 */
public class MyTokenBasedRememberMeServices
		extends
			TokenBasedRememberMeServices {

	private final String LASTACCESS = "lastAccess";
	private final String LOGIN = "login";
	protected final String CURRENTUSER = "currentUser";
	protected final String USERID = "userId";
	private final RequestCache requestCache;
	/**
	 * @deprecated
	 */
	@Deprecated
	public MyTokenBasedRememberMeServices() {
		super();
		setCookieName(CommonParamter.COOK_NAME);
		requestCache = new HttpSessionRequestCache();
	}

	/**
	 * @param key
	 * @param userDetailsService
	 */
	public MyTokenBasedRememberMeServices(String key,
			UserDetailsService userDetailsService) {
		super(key, userDetailsService);
		setCookieName(CommonParamter.COOK_NAME);
		requestCache = new HttpSessionRequestCache();
	}

	private UserService userService;

	private static final ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<HttpServletRequest>();

	public HttpServletRequest getContext() {
		return requestHolder.get();
	}

	public void setContext(HttpServletRequest context) {
		requestHolder.set(context);
	}

	// 工具方法，获取 ip
	protected String getUserIPAddress(HttpServletRequest request) {
		String remoteAddr = request.getRemoteAddr();
		if (remoteAddr.contains(":")) {
			// IPv6,防止在加密时会出错！
			remoteAddr = remoteAddr.replaceAll(":", ".");
		}
		return remoteAddr;
	}

	@Override
	public void onLoginSuccess(HttpServletRequest request,
			HttpServletResponse response,
			Authentication successfulAuthentication) {
		// TODO Auto-generated method stub
		setContext(request);
		super.onLoginSuccess(request, response, successfulAuthentication);
		setContext(null);

	}

	// 将ip地址加入搭配cookie中
	@Override
	protected void setCookie(String[] tokens, int maxAge,
			HttpServletRequest request, HttpServletResponse response) {
		String[] tokensWidthIpAddress = Arrays
				.copyOf(tokens, tokens.length + 1);
		tokensWidthIpAddress[tokensWidthIpAddress.length - 1] = getUserIPAddress(request);
		logger.info("cookie已设置" + tokensWidthIpAddress);
		super.setCookie(tokensWidthIpAddress, maxAge, request, response);
	}

	// 将ip加入转换成 md5的字符串中
	@Override
	protected String makeTokenSignature(long tokenExpiryTime, String username,
			String password) {
		// 加入将ip信息 加入cookie中
		String data = username + ":" + tokenExpiryTime + ":" + password + ":"
				+ getKey() + ":" + getUserIPAddress(getContext());
		return new String(Hex.encode(getDigest().digest(data.getBytes())));
	}

	// 重写处理 自动登陆的代码
	@Override
	protected UserDetails processAutoLoginCookie(String[] cookieTokens,
			HttpServletRequest request, HttpServletResponse response) {
		String ipAddressToken = cookieTokens[cookieTokens.length - 1];
		String[] tokens = Arrays.copyOf(cookieTokens, cookieTokens.length - 1);
		if (!getUserIPAddress(request).equals(ipAddressToken)) {
			throw new InvalidCookieException("IP 不匹配,请重新登陆！");
		}
		setContext(request);
		StringBuilder sb = new StringBuilder();
		for (String token : cookieTokens) {
			sb.append(token + ":");
		}
		logger.info("cookieTokens:" + sb.toString() + new Date());
		UserDetails userDetails = null;
		try {
			userDetails = super.processAutoLoginCookie(tokens, request,
					response);
		} catch (InvalidCookieException e) {
			e.printStackTrace();
			logger.error(e.getMessage());
			throw e;
		}
		if (userDetails != null) {
			try {
				handleSuccessAutoLogin(userDetails, request, response);
			} catch (IOException e) {

				e.printStackTrace();
			} catch (ServletException e) {

				e.printStackTrace();
			}
		}
		// 保存request
		requestCache.saveRequest(request, response);
		return userDetails;
	}

	private MessageDigest getDigest() {
		MessageDigest digest;
		try {
			digest = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			throw new IllegalStateException("No MD5 algorithm available!");
		}
		return digest;
	}

	@Override
	protected void onLoginFail(HttpServletRequest httpservletrequest,
			HttpServletResponse httpservletresponse) {
		// TODO Auto-generated method stub
		super.onLoginFail(httpservletrequest, httpservletresponse);
	}

	@Override
	public void logout(HttpServletRequest request,
			HttpServletResponse response, Authentication authentication) {
		// TODO Auto-generated method stub
		super.logout(request, response, authentication);
	}

	// 自动登录成功后，执行方法
	public void handleSuccessAutoLogin(UserDetails userDetails,
			HttpServletRequest httpservletrequest,
			HttpServletResponse httpservletresponse) throws IOException,
			ServletException {
		CoreUser user = getUserService().findById(userDetails.getUsername());
		HttpSession session = httpservletrequest.getSession();
		session.setAttribute(LOGIN, "true");
		session.setAttribute(CURRENTUSER, user.getNickname());
		session.setAttribute(USERID, user.getUserid());
		session.setAttribute(LASTACCESS, user.getLastvisited());
		user.setLastvisited(new Date());
		user.setLogincount((byte) 0);
		user.setVisitcount(user.getVisitcount() + 1);
		userService.update(user);
	}

	public UserService getUserService() {
		return userService;
	}

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

}
